<?php
declare(strict_types=1);

namespace TairClient\Client;

use Hyperf\Redis\Redis;
use Hyperf\Utils\Codec\Json;

class TairSearch extends TairBase {

    public function __construct(protected Redis $client)
    {

    }



    /**
     * 添加文档
     *
     * 创建索引（index）并添加映射（mappings），映射语法类似ES语法。在添加索引文档前，必须先创建索引。
     */
    public function create(array $params = []) {
        $index = $this->checkArgument($params, 'index');
        $body = $this->checkArgument($params, 'body');
        $bodyString = Json::encode($body);
        $id = $params['id'] ?? '';


        $data = $this->then(function ($index, $bodyString, $id) {
            if (empty($id)) {
                return $this->client->rawCommand('TFT.ADDDOC', $index, $bodyString);
            } else {
                return $this->client->rawCommand('TFT.ADDDOC', $index, $bodyString, 'WITH_ID', $id);
            }
        }, [$index, $bodyString, $id], function ($index, $bodyString, $id) {
            return 'TFT.ADDDOC';
        });
        return Json::decode($data);
    }

    /**
     * 更新文档
     *
     * 更新索引中doc_id指定的文档，若更新的字段为mapping指定的索引字段时，该字段更新的内容需与mapping指定的类型一致；若非索引字段，支持更新任意字段类型的内容。
     * 若更新的字段已存在，则更新原文档，若字段不存在，则新增该字段。若指定的文档不存在，该命令支持自动创建文档，此时效果等同于TFT.ADDDOC
     */
    public function update(array $params = []): bool {
        $index = $this->checkArgument($params, 'index');
        $id = $this->checkArgument($params, 'id');
        $body = $this->checkArgument($params, 'body');
        $bodyString = Json::encode($body);

        return $this->then(function ($index, $id, $bodyString) {
            return $this->client->rawCommand('TFT.UPDATEDOCFIELD', $index, $id, $bodyString);
        }, [$index, $id, $bodyString], function ($index, $id, $bodyString) {
            return 'UPDATEDOCFIELD';
        });
    }


    /**
     * 查询索引中指定doc_id的文档是否存在。
     *
     */
    public function exists(array $params = []): bool {
        $index = $this->checkArgument($params, 'index');
        $id = $this->checkArgument($params, 'id');

        return (bool) $this->then(function ($index, $id) {
            return $this->client->rawCommand('TFT.EXISTS', $index, $id);
        }, [$index, $id], function ($index, $id) {
            return sprintf('TFT.EXISTS %s %s', $index, $id);
        });
    }

    /**
     * 根据query语句搜索索引的文档，query语法类似ES语法。
     *
     */
    public function search(array $params = []): array {
        $index = $this->checkArgument($params, 'index');
        $body = $this->checkArgument($params, 'body');

        $data = $this->then(function ($index, $body) {
            return $this->client->rawCommand('TFT.SEARCH', $index, $body);
        }, [$index, $body], function ($index, $body) {
            return sprintf('TFT.SEARCH %s %s', $index, $body);
        });
        return Json::decode($data);
    }

    /**
     * 获取索引中指定doc_id的文档内容。
     */
    public function get(array $params = []) {
        $index = $this->checkArgument($params, 'index');
        $id = $this->checkArgument($params, 'id');

        $data = $this->then(function ($index, $id) {
            return $this->client->rawCommand('TFT.GETDOC', $index, $id);
        }, [$index, $id], function ($index, $id) {
            return sprintf('TFT.GETDOC %s %s', $index, $id);
        });
        return Json::decode($data);
    }

    /**
     * 添加多个文档
     * 如果ID重复会进行覆盖
     *
     */
    public function createMany(array $params = []): bool {
        $index = $this->checkArgument($params, 'index');
        $bodys = $this->checkArgument($params, 'bodys');
        $bodyMany = [];
        foreach ($bodys as &$body) {
            $bodyMany[] = Json::encode($body['body']);
            $bodyMany[] = Json::encode($body['id']);
        }
        return $this->then(function ($index, $bodyMany) {
           return $this->client->rawCommand('TFT.MADDDOC', $index, ...$bodyMany);
        }, [$index, $bodyMany], function ($index) {
            return sprintf('TFT.MADDDOC %s', $index);
        });
    }

    /**
     * 创建索引
     */
    public function createIndex(string $index, array $mapping) {
        $this->checkNotEmpty($index);
        $this->checkNotEmpty($mapping);

        return $this->then(function ($index, &$mapping) {
            return $this->client->rawCommand('TFT.CREATEINDEX', $index, Json::encode($mapping));
        }, [$index, &$mapping], function ($index, &$mapping) {
            return 'TFT.CREATEINDEX';
        });
    }

    /**
     * 检查索引是否存在
     */
    public function existsIndex(string $index): bool {
        $this->checkNotEmpty($index);
        return (bool) $this->client->exists($index);
    }

    /**
     * 删除多个文档
     */
    public function deleteMany(array $params = []) {
        $index = $this->checkArgument($params, 'index');
        $this->checkNotEmpty($index);

        $ids = $this->checkArgument($params, 'ids');
        $this->checkNotEmpty($ids);

        return $this->then(function ($index, $ids) {
            return $this->client->rawCommand('TFT.DELDOC', $index, ...$ids);
        }, [$index, $ids], function ($index, $ids) {
            return sprintf('TFT.DELDOC %s %s', $index, implode(' ', $ids));
        });
    }

    /**
     * 删除文档的字段
     */
    public function deleteDocField(array $params = []) {
        $index = $this->checkArgument($params, 'index');
        $this->checkNotEmpty($index);

        $id = $this->checkArgument($params, 'id');
        $this->checkNotEmpty($id);

        $fields = $this->checkArgument($params, 'fields');
        $this->checkNotEmpty($fields);

        return $this->then(function ($index, $id, $fields) {
            return $this->client->rawCommand('TFT.DELDOCFIELD', $index, $id, ...$fields);
        }, [$index, $id, $fields], function ($index, $id, $fields) {
            return sprintf('TFT.DELDOCFIELD %s %s %s', $index, $id, implode(' ', $fields));
        });
    }
}